/*
 *
 *  Copyright (C) 2012-2024
 *  All rights reserved, Designed By 杭州连线客信息技术有限公司
 *  注意：
 *  本软件为杭州连线客信息技术有限公司开发研制，未经购买不得使用
 *  购买后可获得全部源代码（禁止转卖、分享、上传到码云、github等开源平台）
 *  一经发现盗用、分享等行为，将追究法律责任，后果自负
 *
 */

package com.lxk.pay.controller;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.api.internal.util.AlipaySignature;
import com.ijpay.alipay.AliPayApiConfig;
import com.ijpay.alipay.AliPayApiConfigKit;
import com.ijpay.core.kit.WxPayKit;
import com.lxk.pay.alipay.constant.AliConstants;
import com.lxk.pay.wx.contants.WxConstants;
import com.lxk.service.oms.api.OrderNotifyServiceApi;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.common.web.core.BaseController;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 支付回调
 *
 * @author huahua
 * @date 2024-02-28
 */
@SaIgnore
@Validated
@RequiredArgsConstructor
@RestController
@Slf4j
@RequestMapping("/call/back")
public class PayNotifyController extends BaseController {

    private final OrderNotifyServiceApi notifyServiceApi;


    /**
     * 微信支付退款异步通知
     */
    @RequestMapping(value = "/wx/refund/{tenantId}/{appId}", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public String refundPayNotify(@PathVariable("tenantId") String tenantId,
                            @PathVariable("appId") String appId, HttpServletRequest request) {
        try {
            TenantHelper.setDynamic(tenantId);
            // TODO 如何获取租户信息
            String xmlMsg = this.readData(request);
            log.info("微信支付退款异步通知={}", xmlMsg);
            Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);
            String returnCode = params.get(WxConstants.RETURN_CODE);
            // 注意重复通知的情况，同一订单号可能收到多次通知，请注意一定先判断订单状态
            // 注意此处签名方式需与统一下单的签名类型一致
            if (WxPayKit.codeIsOk(returnCode)) {
                // 更新订单信息
                String outTradeNo = params.get(WxConstants.OUT_TRADE_NO);
                String nonceStr = params.get(WxConstants.NONCE_STR);
                String transactionId = params.get(WxConstants.TRANSACTION_ID);
                String attach = params.get(WxConstants.ATTACH);
                log.info("微信支付退款异步通知  更新订单信息 nonce_str:{},更新订单信息 out_trade_no:{},更新订单信息 transaction_id:{},更新订单信息 attach:{}", nonceStr, outTradeNo, transactionId, attach);
                Map<String, String> xml = new HashMap<>();
                xml.put(WxConstants.RETURN_CODE, WxConstants.SUCCESS);
                xml.put(WxConstants.RETURN_MSG, WxConstants.OK);
                return WxPayKit.toXml(xml);
            }
        } finally {
            TenantHelper.clearDynamic();
        }
        return null;
    }


    /**
     * 微信支付异步通知
     */
    @RequestMapping(value = "/wx/payNotifyUrl/{tenantId}/{appId}", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public String payNotify(@PathVariable("tenantId") String tenantId,
                            @PathVariable("appId") String appId, HttpServletRequest request) {
        try {
            TenantHelper.setDynamic(tenantId);
            // TODO 如何获取租户信息
            String xmlMsg = this.readData(request);
            log.info("支付通知={}", xmlMsg);
            Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);
            String returnCode = params.get(WxConstants.RETURN_CODE);
            // 注意重复通知的情况，同一订单号可能收到多次通知，请注意一定先判断订单状态
            // 注意此处签名方式需与统一下单的签名类型一致
            if (WxPayKit.codeIsOk(returnCode)) {
                // 更新订单信息
                String outTradeNo = params.get(WxConstants.OUT_TRADE_NO);
                String nonceStr = params.get(WxConstants.NONCE_STR);
                String transactionId = params.get(WxConstants.TRANSACTION_ID);
                String attach = params.get(WxConstants.ATTACH);
                log.info("更新订单信息 nonce_str:{},更新订单信息 out_trade_no:{},更新订单信息 transaction_id:{},更新订单信息 attach:{}", nonceStr, outTradeNo, transactionId, attach);
                notifyServiceApi.notifySuccess(outTradeNo, nonceStr, attach);
                // 发送通知等
                Map<String, String> xml = new HashMap<String, String>(2);
                xml.put(WxConstants.RETURN_CODE, WxConstants.SUCCESS);
                xml.put(WxConstants.RETURN_MSG, WxConstants.OK);
                return WxPayKit.toXml(xml);
            }
        } finally {
            TenantHelper.clearDynamic();
        }
        return null;
    }

    /**
     * 支付宝异步通知
     */
    @RequestMapping(value = "/ali/payNotifyUrl/{tenantId}/{appId}", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public String aliPayNotify(@PathVariable("tenantId") String tenantId,
                               @PathVariable("appId") String appId,
                               HttpServletRequest request) {
        String paramsJson = null;
        try {
            TenantHelper.setDynamic(tenantId);
            Map<String, String> params = convertRequestParamsToMap(request); // 将异步通知中收到的待验证所有参数都存放到map中
            paramsJson = JSON.toJSONString(params);
            log.info("支付宝回调 = " + paramsJson);
            //商户订单号
            String outTradeNo = params.get(AliConstants.OUT_TRADE_NO);
            String outRequestNo = params.get(AliConstants.OUT_REQUEST_NO);
            // 判断是否是退款订单
            String refundFee = params.get(AliConstants.REFUND_FEE);
            if (StrUtil.isNotBlank(refundFee)) {
                // 订单退款
            }
            // 判断订单类型
//            String passbackParams = params.get(AliConstants.PASSBACK_PARAMS);
            //交易状态
            String tradeStatus = params.get(AliConstants.TRADE_STATUS);
            // 调用SDK验证签名
            AliPayApiConfig config = AliPayApiConfigKit.getAliPayApiConfig();
            boolean signVerified = AlipaySignature.certVerifyV1(params, config.getAliPayCertPath(), AliConstants.UTF8, AliConstants.RSA2);
            if (signVerified) {//验证成功
                log.info("支付宝回调签名认证成功");
                if (tradeStatus.equals(AliConstants.TRADE_FINISHED) || tradeStatus.equals(AliConstants.TRADE_SUCCESS)) {//交易成功
                    notifyServiceApi.notifySuccess(outTradeNo, outRequestNo, null);
                }
                return AliConstants.SUCCESS;
            } else {
                log.info("支付宝回调签名认证失败，signVerified=false, paramsJson:{}", paramsJson);
                return AliConstants.FAIL;
            }
        } catch (Exception e) {
            log.error("支付宝回调签名认证失败,paramsJson:{},", paramsJson, e);
            return AliConstants.FAIL;
        } finally {
            TenantHelper.clearDynamic();
        }
    }

    /**
     * 读取微信回调xml
     *
     * @param request
     * @return
     */
    public String readData(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            StringBuilder result = new StringBuilder();
            br = request.getReader();
            for (String line; (line = br.readLine()) != null; ) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append(line);
            }
            return result.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 将request中的参数转换成Map
     *
     * @param request
     * @return
     */
    private Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
        Map<String, String> retMap = new HashMap<String, String>();
        Map requestParams = request.getParameterMap();
        for (Object o : requestParams.keySet()) {
            String name = (String) o;
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                    : valueStr + values[i] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
            retMap.put(name, valueStr);
        }
        return retMap;
    }

}
